package org.nutz.integration.shiro.realm;
import com.ht.sps.nh.entity.User;
import com.ht.sps.nh.service.NhUserService;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.SimplePrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.nutz.integration.Webs;
import org.nutz.ioc.Ioc;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.Lang;
import org.nutz.lang.Strings;

import java.io.Serializable;
import java.util.List;
import java.util.Objects;

/**
 * 用NutDao来实现Shiro的Realm
 * <p/>
 * 可以通过配置文件注入数据源
 * <p/>
 * 在Web环境中也可以通过自动搜索来获取NutDao
 * 
 * @author wendal
 * 
 */
@IocBean
public class NutDaoRealm extends AuthorizingRealm {

    @Inject
	private NhUserService nhUserService;

	private NhUserService getUserService() {
		if (nhUserService == null) {
			Ioc ioc = Webs.ioc();
            nhUserService = ioc.get(NhUserService.class);
			return nhUserService;
		}
		return nhUserService;
	}

    /**
     * 授权查询回调函数, 进行鉴权但缓存中无用户的授权信息时调用.
     */
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(
			PrincipalCollection principalCollection) {
		String username = principalCollection.getPrimaryPrincipal().toString();
		User user = getUserService().fetchByName(username);
		if (user == null)
			return null;
		if (user.getAccountLocked())
			throw new LockedAccountException("Account [" + username
					+ "] is locked.");
		SimpleAuthorizationInfo auth = new SimpleAuthorizationInfo();
		return auth;
	}

    /**
     * 身份认证回调函数,登录时自动调用.<br/>
     * <code>
     * Subject currentUser = SecurityUtils.getSubject();<br/>
     * UsernamePasswordToken token = new UsernamePasswordToken( username, password);<br/>
     * token.setRememberMe(true);<br/>
     * currentUser.login(token);<br/>
     * </code>
     */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(
			AuthenticationToken token) throws AuthenticationException {
		UsernamePasswordToken upToken = (UsernamePasswordToken) token;
		User user = getUserService().fetchByName(upToken.getUsername());
		if (Lang.isEmpty(user)) {
			return null;
        }
		if (user.getAccountLocked()) {
			throw new LockedAccountException("Account ["
					+ upToken.getUsername() + "] is locked.");
        }
		SimpleAuthenticationInfo account = new SimpleAuthenticationInfo(
                new ShiroUser(user.getId(), user.getUsername(), user.getName()),
				user.getPassword(), getName());
		if (!Strings.isEmpty(user.getSalt())) {
			ByteSource salt = ByteSource.Util.bytes(user.getSalt());
			account.setCredentialsSalt(salt);
		}
		return account;
	}

	/**
	 * 更新用户授权信息缓存.
	 */
	public void clearCachedAuthorizationInfo(String principal) {
		SimplePrincipalCollection principals = new SimplePrincipalCollection(
				principal, getName());
		clearCachedAuthorizationInfo(principals);
	}

	/**
	 * 清除所有用户授权信息缓存.
	 */
	public void clearAllCachedAuthorizationInfo() {
		Cache<Object, AuthorizationInfo> cache = getAuthorizationCache();
		if (cache != null) {
			for (Object key : cache.keys()) {
				cache.remove(key);
			}
		}
	}

    /**
     * 自定义Authentication对象，使得Subject除了携带用户的登录名外还可以携带更多信息.
     */
    public static class ShiroUser implements Serializable {

        public Integer id;
        public String username;
        public String name;

        public ShiroUser(Integer id, String username, String name) {
            this.id = id;
            this.username = username;
            this.name = name;
        }

        public String getName() {
            return name;
        }

        public Integer getId() {
            return id;
        }

        public void setId(Integer id) {
            this.id = id;
        }

        public String getUsername() {
            return username;
        }

        public void setUsername(String username) {
            this.username = username;
        }

        public void setName(String name) {
            this.name = name;
        }

        /**
         * 本函数输出将作为默认的<shiro:principal/>输出.
         */
        @Override
        public String toString() {
            return username;
        }

        /**
         * 重载hashCode,只计算loginName;
         */
        @Override
        public int hashCode() {
            return Objects.hashCode(username);
        }

        /**
         * 重载equals,只计算loginName;
         */
        @Override
        public boolean equals(Object obj) {
            if (this == obj) {
                return true;
            }
            if (obj == null) {
                return false;
            }
            if (getClass() != obj.getClass()) {
                return false;
            }
            ShiroUser other = (ShiroUser) obj;
            if (username == null) {
                if (other.username != null) {
                    return false;
                }
            } else if (!username.equals(other.username)) {
                return false;
            }
            return true;
        }
    }
}
